home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / DB / odbtp.php < prev    next >
Encoding:
PHP Script  |  2005-12-02  |  40.6 KB  |  1,294 lines

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  4.  
  5. /**
  6.  * The PEAR DB driver for the odbtp extension
  7.  * for remotely interacting with Win32-based databases from
  8.  * any platform.  The odbtp extension is available at
  9.  * http://odbtp.sourceforge.net.
  10.  *
  11.  * PHP versions 4 and 5
  12.  *
  13.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  14.  * that is available through the world-wide-web at the following URI:
  15.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  16.  * the PHP License and are unable to obtain it through the web, please
  17.  * send a note to license@php.net so we can mail you a copy immediately.
  18.  *
  19.  * @category   Database
  20.  * @package    DB_odbtp
  21.  * @author     Robert Twitty <rtwitty@users.sourceforge.net>
  22.  * @copyright  1997-2005 The PHP Group
  23.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  24.  * @version    CVS: $Id: odbtp.php,v 1.6 2005/03/13 00:54:42 rtwitty Exp $
  25.  * @link       http://pear.php.net/package/DB_odbtp
  26.  */
  27.  
  28. /**
  29.  * Obtain the DB_common class so it can be extended from
  30.  */
  31. require_once 'DB/common.php';
  32.  
  33. /**
  34.  * Database independent query interface definition for ODBTP extension.
  35.  *
  36.  * @package  DB_odbtp
  37.  * @version  $Id: odbtp.php,v 1.6 2005/03/13 00:54:42 rtwitty Exp $
  38.  * @category Database
  39.  * @author   Robert Twitty <rtwitty@users.sourceforge.net>
  40.  */
  41.  
  42. /**
  43.  * The methods PEAR DB uses to interact with the odbtp extension
  44.  * for remotely interacting with Win32-based databases.
  45.  *
  46.  * These methods overload the ones declared in DB_common.
  47.  *
  48.  * @category   Database
  49.  * @package    DB_odbtp
  50.  * @author     Robert Twitty <rtwitty@users.sourceforge.net>
  51.  * @copyright  1997-2005 The PHP Group
  52.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  53.  * @version    Release: 1.0.3
  54.  * @link       http://pear.php.net/package/DB_odbtp
  55.  */
  56. class DB_odbtp extends DB_common
  57. {
  58.     // {{{ properties
  59.  
  60.     /**
  61.      * The DB driver type (mysql, oci8, odbc, etc.)
  62.      * @var string
  63.      * @access public
  64.      */
  65.     var $phptype = 'odbtp';
  66.  
  67.     /**
  68.      * DSN used to establish connection.
  69.      * @var array
  70.      * @access public
  71.      * @see DB::parseDSN()
  72.      */
  73.     var $dsn = array();
  74.  
  75.     /**
  76.      * Database used with regards to SQL syntax, ODBC driver, etc.
  77.      * @var string
  78.      * @access public
  79.      * @see connect(), toString()
  80.      */
  81.     var $dbsyntax = 'unknown';
  82.  
  83.     /**
  84.      * The capabilities of this DB implementation
  85.      *
  86.      * The 'new_link' element contains the PHP version that first provided
  87.      * new_link support for this DBMS.  Contains false if it's unsupported.
  88.      *
  89.      * Meaning of the 'limit' element:
  90.      *   + 'emulate' = emulate with fetch row by number
  91.      *   + 'alter'   = alter the query
  92.      *   + false     = skip rows
  93.      *
  94.      * @var array
  95.      * @access private
  96.      */
  97.     var $features = array(
  98.         'limit'         => 'emulate',
  99.         'new_link'      => false,
  100.         'numrows'       => true,
  101.         'pconnect'      => true,
  102.         'prepare'       => false,
  103.         'ssl'           => false,
  104.         'transactions'  => false
  105.     );
  106.  
  107.     /**
  108.      * A mapping of native error codes to DB error codes
  109.      * @var array
  110.      * @access private
  111.      */
  112.     var $errorcode_map = array(
  113.         '01004' => DB_ERROR_TRUNCATED,
  114.         '07001' => DB_ERROR_MISMATCH,
  115.         '21S01' => DB_ERROR_MISMATCH,
  116.         '21S02' => DB_ERROR_MISMATCH,
  117.         '22003' => DB_ERROR_INVALID_NUMBER,
  118.         '22007' => DB_ERROR_INVALID_DATE,
  119.         '22018' => DB_ERROR_INVALID_NUMBER,
  120.         '22012' => DB_ERROR_DIVZERO,
  121.         '23000' => DB_ERROR_CONSTRAINT,
  122.         '23503' => DB_ERROR_CONSTRAINT,
  123.         '24000' => DB_ERROR_INVALID,
  124.         '34000' => DB_ERROR_INVALID,
  125.         '42000' => DB_ERROR_SYNTAX,
  126.         '42S01' => DB_ERROR_ALREADY_EXISTS,
  127.         '42S02' => DB_ERROR_NOSUCHTABLE,
  128.         '42S11' => DB_ERROR_ALREADY_EXISTS,
  129.         '42S12' => DB_ERROR_NOT_FOUND,
  130.         '42S21' => DB_ERROR_ALREADY_EXISTS,
  131.         '42S22' => DB_ERROR_NOSUCHFIELD,
  132.         '08004' => DB_ERROR_CONNECT_FAILED,
  133.         '08007' => DB_ERROR_CONNECT_FAILED,
  134.         '08S01' => DB_ERROR_CONNECT_FAILED,
  135.         'HY009' => DB_ERROR_INVALID,
  136.         'HY024' => DB_ERROR_INVALID,
  137.         'HY090' => DB_ERROR_INVALID,
  138.         'IM001' => DB_ERROR_UNSUPPORTED,
  139.         'ODBTPINV' => DB_ERROR_INVALID
  140.     );
  141.  
  142.     /**
  143.      * ODBTP connection resource
  144.      * @var resource
  145.      * @access private
  146.      */
  147.     var $connection;
  148.  
  149.     /**
  150.      * ODBTP query result resource
  151.      * @var resource
  152.      * @access private
  153.      */
  154.     var $query_result;
  155.  
  156.     /**
  157.      * Transaction isolation level
  158.      * @var int
  159.      * @access private
  160.      */
  161.     var $txn_isolation;
  162.  
  163.     // }}}
  164.     // {{{ constructor
  165.  
  166.     /**
  167.      * This constructor calls <kbd>$this->DB_common()</kbd>
  168.      *
  169.      * @return void
  170.      *
  171.      * @access public
  172.      * @see DB::common()
  173.      */
  174.     function DB_odbtp()
  175.     {
  176.         $this->DB_common();
  177.         $this->errorcode_map = array(
  178.         );
  179.     }
  180.  
  181.     // }}}
  182.     // {{{ connect()
  183.  
  184.     /**
  185.      * Connect to a database via an ODBTP server
  186.      *
  187.      * The format of the supplied DSN:
  188.      *
  189.      *   odbtp(dbsyntax)://username:password@odbtphost/database
  190.      *
  191.      * or
  192.      *
  193.      *   odbtp://username:password@odbtpinterface/database
  194.      *
  195.      * Examples:
  196.      *
  197.      *  odbtp(access)://myuid:mypwd@odbtp.somewhere.com/c:\mydb.mdb
  198.      *  odbtp(mssql)://myuid:mypwd@odbtp.somewhere.com/mydb?server=mysqlsrv
  199.      *  odbtp://myuid:mypwd@myinterface/mydb
  200.      *
  201.      * @param array $dsninfo data source name info returned by DB::parseDSN
  202.      * @param boolean $persistent kept for interface compatibility
  203.      *
  204.      * @return int DB_OK if successful, or DB error code if failure
  205.      *
  206.      * @see DB::parseDSN()
  207.      * @access public
  208.      */
  209.     function connect($dsninfo, $persistent = false)
  210.     {
  211.         if (!PEAR::loadExtension('odbtp')) {
  212.             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  213.         }
  214.         $this->dsn = $dsninfo;
  215.         $this->dbsyntax = 'unknown';
  216.         $this->txn_isolation = ODB_TXN_DEFAULT;
  217.         $odbcinfo = array();
  218.         $hostspec = '';
  219.         $port = '';
  220.         $conntype = 'normal';
  221.         $rowcache = false;
  222.         $unicode = false;
  223.         $vardatasize = 0;
  224.         $connid = '';
  225.         $dbparam = 'DATABASE';
  226.  
  227.         foreach ($dsninfo as $option => $value) {
  228.             $option = strtoupper($option);
  229.  
  230.             switch ($option) {
  231.                 // PEAR DB DSN specific options
  232.                 case 'PHPTYPE':
  233.                     $this->phptype = $value; break;
  234.                 case 'DBSYNTAX':
  235.                     $this->dbsyntax = strtolower($value);
  236.                     switch( $this->dbsyntax ) {
  237.                         case 'mssql':
  238.                             $dbparam = 'DATABASE';
  239.                             if (!isset($odbcinfo['DRIVER']))
  240.                                 $odbcinfo['DRIVER'] = '{SQL Server}';
  241.                             if (!isset($odbcinfo['SERVER']))
  242.                                 $odbcinfo['SERVER'] = '(local)';
  243.                             break;
  244.                         case 'access':
  245.                             $dbparam = 'DBQ';
  246.                             if (!isset($odbcinfo['DRIVER']))
  247.                                 $odbcinfo['DRIVER'] = '{Microsoft Access Driver (*.mdb)}';
  248.                             if (!isset($odbcinfo['UID']))
  249.                                 $odbcinfo['UID'] = 'admin';
  250.                             if (!isset($odbcinfo['PWD']))
  251.                                 $odbcinfo['PWD'] = '';
  252.                             break;
  253.                         case 'vfp':
  254.                             $dbparam = 'SOURCEDB';
  255.                             if (!isset($odbcinfo['DRIVER']))
  256.                                 $odbcinfo['DRIVER'] = '{Microsoft Visual FoxPro Driver}';
  257.                             if (!isset($odbcinfo['SOURCETYPE']))
  258.                                 $odbcinfo['SOURCETYPE'] = 'DBF';
  259.                             if (!isset($odbcinfo['EXCLUSIVE']))
  260.                                 $odbcinfo['EXCLUSIVE'] = 'NO';
  261.                             break;
  262.                         case 'oracle':
  263.                             $dbparam = 'DBQ';
  264.                             if (!isset($odbcinfo['DRIVER']))
  265.                                 $odbcinfo['DRIVER'] = '{Oracle ODBC Driver}';
  266.                             break;
  267.                         case 'sybase':
  268.                             $dbparam = 'DATABASE';
  269.                             if (!isset($odbcinfo['DRIVER']))
  270.                                 $odbcinfo['DRIVER'] = '{Sybase ASE ODBC Driver}';
  271.                             if (!isset($odbcinfo['SRVR']))
  272.                                 $odbcinfo['SRVR'] = 'localhost';
  273.                             break;
  274.                         case 'db2':
  275.                             $dbparam = 'DATABASE';
  276.                             if (!isset($odbcinfo['DRIVER']))
  277.                                 $odbcinfo['DRIVER'] = '{IBM DB2 ODBC Driver}';
  278.                             if (!isset($odbcinfo['HOSTNAME']))
  279.                                 $odbcinfo['HOSTNAME'] = 'localhost';
  280.                             if (!isset($odbcinfo['PORT']))
  281.                                 $odbcinfo['PORT'] = '50000';
  282.                             if (!isset($odbcinfo['PROTOCOL']))
  283.                                 $odbcinfo['PROTOCOL'] = 'TCPIP';
  284.                             break;
  285.                         case 'mysql':
  286.                             $dbparam = 'DATABASE';
  287.                             if (!isset($odbcinfo['DRIVER']))
  288.                                 $odbcinfo['DRIVER'] = '{mySQL}';
  289.                             if (!isset($odbcinfo['SERVER']))
  290.                                 $odbcinfo['SERVER'] = 'localhost';
  291.                             if (!isset($odbcinfo['PORT']))
  292.                                 $odbcinfo['PORT'] = '3306';
  293.                             if (!isset($odbcinfo['OPTION']))
  294.                                 $odbcinfo['OPTION'] = '131072';
  295.                             if (!isset($odbcinfo['STMT']))
  296.                                 $odbcinfo['STMT'] = '';
  297.                             break;
  298.                         case 'text':
  299.                             $dbparam = 'DBQ';
  300.                             if (!isset($odbcinfo['DRIVER']))
  301.                                 $odbcinfo['DRIVER'] = '{Microsoft Text Driver (*.txt; *.csv)}';
  302.                             if (!isset($odbcinfo['EXTENSIONS']))
  303.                                 $odbcinfo['EXTENSIONS'] = 'asc,csv,tab,txt';
  304.                             break;
  305.                         case 'excel':
  306.                             $dbparam = 'DBQ';
  307.                             if (!isset($odbcinfo['DRIVER']))
  308.                                 $odbcinfo['DRIVER'] = '{Microsoft Excel Driver (*.xls)}';
  309.                             if (!isset($odbcinfo['DRIVERID']))
  310.                                 $odbcinfo['DRIVERID'] = '790';
  311.                             break;
  312.                         case 'dsn':
  313.                             $dbparam = 'DSN';
  314.                             if (!isset($odbcinfo['DSN']))
  315.                                 $odbcinfo['DSN'] = '';
  316.                             $this->dbsyntax = 'unknown';
  317.                             break;
  318.                         default:
  319.                             $this->dbsyntax = 'unknown';
  320.                     }
  321.                     break;
  322.                 case 'USERNAME':
  323.                     $username = $value; break;
  324.                 case 'PASSWORD':
  325.                     $password = $value; break;
  326.                 case 'PROTOCOL':
  327.                     $protocol = $value; break;
  328.                 case 'SOCKET':
  329.                     $socket = $value; break;
  330.                 case 'HOSTSPEC':
  331.                     $hostspec = $value; break;
  332.                 case 'PORT':
  333.                     $port = $value; break;
  334.                 case 'DATABASE':
  335.                     $database = $value; break;
  336.  
  337.                 // ODBTP specific options
  338.                 case 'CONNTIMEOUT':
  339.                     $conntimeout = $value; break;
  340.                 case 'READTIMEOUT':
  341.                     $readtimeout = $value; break;
  342.                 case 'CONNTYPE':
  343.                     $conntype = strtolower($value); break;
  344.                 case 'ROWCACHE':
  345.                     $rowcache = strtolower($value); break;
  346.                 case 'UNICODE':
  347.                     $unicode = strtolower($value); break;
  348.                 case 'TXNISOL':
  349.                     switch (strtolower($value)) {
  350.                         case 'readuncommitted':
  351.                             $this->txn_isolation = ODB_TXN_READUNCOMMITTED;
  352.                             break;
  353.                         case 'readcommitted':
  354.                             $this->txn_isolation = ODB_TXN_READCOMMITTED;
  355.                             break;
  356.                         case 'repeatableread':
  357.                             $this->txn_isolation = ODB_TXN_REPEATABLEREAD;
  358.                             break;
  359.                         case 'serializable':
  360.                             $this->txn_isolation = ODB_TXN_SERIALIZABLE;
  361.                             break;
  362.                         default:
  363.                             $this->txn_isolation = ODB_TXN_DEFAULT;
  364.                     }
  365.                 case 'VARDATASIZE':
  366.                     $vardatasize = intval($value); break;
  367.                 case 'CONNID':
  368.                     $connid = strtolower($value); break;
  369.                 case 'DBPARAM':
  370.                     $dbparam = strtoupper($value); break;
  371.                 case 'ODBCPROTOCOL':
  372.                     $odbcinfo['PROTOCOL'] = $value; break;
  373.                 case 'ODBCPORT':
  374.                     $odbcinfo['PORT'] = $value; break;
  375.  
  376.                 // Unrecognized options are considered to be ODBC specific
  377.                 default:
  378.                     $odbcinfo[$option] = $value;
  379.             }
  380.         }
  381.         if ($conntype == 'reserved') {
  382.             $connect_function = 'odbtp_rconnect';
  383.         } else if ($conntype == 'single') {
  384.             $connect_function = 'odbtp_sconnect';
  385.         } else {
  386.             $connect_function = 'odbtp_connect';
  387.         }
  388.         if( !$hostspec ) $hostspec = '127.0.0.1';
  389.  
  390.         if ($port) {
  391.             $hostspec .= ':' . $port;
  392.         }
  393.         if (isset($conntimeout)) {
  394.             if ($port)
  395.                 $hostspec .= ':' . $conntimeout;
  396.             else
  397.                 $hostspec .= '::' . $conntimeout;
  398.         }
  399.         if (isset($readtimeout)) {
  400.             if ($port && isset($conntimeout))
  401.                 $hostspec .= ':' . $readtimeout;
  402.             else if ($port)
  403.                 $hostspec .= '::' . $readtimeout;
  404.             else
  405.                 $hostspec .= ':::' . $readtimeout;
  406.         }
  407.         if (count($odbcinfo) != 0) {
  408.             if (is_string($username)) $odbcinfo['UID'] = $username;
  409.             if (is_string($password)) $odbcinfo['PWD'] = $password;
  410.             if (is_string($database)) $odbcinfo[$dbparam] = $database;
  411.  
  412.             $odbc_connect = '';
  413.             foreach ($odbcinfo as $option => $value) {
  414.                 $odbc_connect .= "$option=$value;";
  415.             }
  416.             $conn = @$connect_function($hostspec, $odbc_connect);
  417.         } else if ($connid) {
  418.             $conn = @$connect_function($hostspec, $connid);
  419.         } else {
  420.             $conn = @$connect_function($hostspec, $username, $password, $database);
  421.         }
  422.         if (!$conn) {
  423.             return $this->raiseError(DB_ERROR_CONNECT_FAILED,
  424.                                      null, null, null, @odbtp_last_error());
  425.         }
  426.         if ($connect_function == 'odbtp_connect' && !$persistent) {
  427.             @odbtp_dont_pool_dbc($conn);
  428.         }
  429.         switch ($rowcache) {
  430.             case 1:
  431.             case 'true':
  432.             case 'yes':
  433.                 if (!@odbtp_use_row_cache($conn)) {
  434.                     return $this->raiseError(DB_ERROR, null, null, null,
  435.                                              @odbtp_last_error());
  436.                 }
  437.         }
  438.         switch ($unicode) {
  439.             case 1:
  440.             case 'true':
  441.             case 'yes':
  442.                 if (!@odbtp_set_attr(ODB_ATTR_UNICODESQL, 1, $conn)) {
  443.                     return $this->raiseError(DB_ERROR, null, null, null,
  444.                                              @odbtp_last_error());
  445.                 }
  446.         }
  447.         if ($vardatasize) {
  448.             if (!@odbtp_set_attr(ODB_ATTR_VARDATASIZE, $vardatasize, $conn)) {
  449.                 return $this->raiseError(DB_ERROR, null, null, null,
  450.                                          @odbtp_last_error());
  451.             }
  452.         }
  453.         if (!@odbtp_set_attr(ODB_ATTR_FULLCOLINFO, 1, $conn)) {
  454.             return $this->raiseError(DB_ERROR, null, null, null,
  455.                                      @odbtp_last_error());
  456.         }
  457.         if( $this->dbsyntax == 'unknown' ) {
  458.             switch (@odbtp_get_attr(ODB_ATTR_DRIVER, $conn)) {
  459.                 case ODB_DRIVER_MSSQL:
  460.                     $this->dbsyntax = 'mssql'; break;
  461.                 case ODB_DRIVER_FOXPRO:
  462.                     $this->dbsyntax = 'vfp'; break;
  463.                 case ODB_DRIVER_ORACLE:
  464.                     $this->dbsyntax = 'oracle'; break;
  465.                 case ODB_DRIVER_SYBASE:
  466.                     $this->dbsyntax = 'sybase'; break;
  467.                 case ODB_DRIVER_MYSQL:
  468.                     $this->dbsyntax = 'mysql'; break;
  469.                 default:
  470.                     $dbms = strtolower(@odbtp_get_attr(ODB_ATTR_DBMSNAME, $conn));
  471.                     if ($dbms == 'access')
  472.                         $this->dbsyntax = 'access';
  473.                     else if ($dbms == 'text')
  474.                         $this->dbsyntax = 'text';
  475.                     else if ($dbms == 'excel')
  476.                         $this->dbsyntax = 'excel';
  477.                     else if (strncmp($dbms, 'db2', 3) == 0)
  478.                         $this->dbsyntax = 'db2';
  479.                     else
  480.                         $this->dbsyntax = $dbms;
  481.             }
  482.         }
  483.         switch ($this->dbsyntax) {
  484.             case 'access':
  485.             case 'vfp':
  486.             case 'text':
  487.             case 'excel':
  488.                 $tc = false;
  489.                 break;
  490.             case 'mssql':
  491.             case 'oracle':
  492.             case 'sybase':
  493.             case 'db2':
  494.                 $tc = true;
  495.                 break;
  496.             default:
  497.                 $tc = @odbtp_get_attr(ODB_ATTR_TXNCAPABLE, $conn);
  498.         }
  499.         $this->features['transactions'] = $tc ? true : false;
  500.  
  501.         $this->connection = $conn;
  502.  
  503.         return DB_OK;
  504.     }
  505.  
  506.     // }}}
  507.     // {{{ disconnect()
  508.  
  509.     /**
  510.      * Disconnect from ODBTP server
  511.      *
  512.      * @return boolean TRUE if successful, otherwise FALSE
  513.      *
  514.      * @access public
  515.      */
  516.     function disconnect()
  517.     {
  518.         $ret = @odbtp_close($this->connection);
  519.         $this->connection = null;
  520.         return $ret;
  521.     }
  522.  
  523.     // }}}
  524.     // {{{ simpleQuery()
  525.  
  526.     /**
  527.      * Perform a SQL query.
  528.      *
  529.      * @param string $query SQL statement
  530.      *
  531.      * @return mixed ODBTP query result resource, DB_OK or DB_Error
  532.      *
  533.      * @access public
  534.      */
  535.     function simpleQuery($query)
  536.     {
  537.         $ismanip = DB::isManip($query);
  538.         $this->last_query = $query;
  539.         $query = $this->modifyQuery($query);
  540.         $this->query_result = @odbtp_query($query, $this->connection);
  541.         if (!$this->query_result) {
  542.             return $this->odbtpRaiseError();
  543.         }
  544.         // Determine which queries that should return data, and which
  545.         // should return an error code only.
  546.         return $ismanip ? DB_OK : $this->query_result;
  547.     }
  548.  
  549.     // }}}
  550.     // {{{ nextResult()
  551.  
  552.     /**
  553.      * Move the internal odbtp result pointer to the next available result
  554.      *
  555.      * @param resource $result ODBTP query result identifier
  556.      *
  557.      * @return boolean TRUE if a result is available, otherwise FALSE
  558.      *
  559.      * @access public
  560.      */
  561.     function nextResult($result)
  562.     {
  563.         return @odbtp_next_result($result);
  564.     }
  565.  
  566.     // }}}
  567.     // {{{ fetchInto()
  568.  
  569.     /**
  570.      * Fetch a row and insert the data into an existing array.
  571.      *
  572.      * Formating of the array and the data therein are configurable.
  573.      * See DB_result::fetchInto() for more information.
  574.      *
  575.      * @param resource $result    ODBTP query result identifier
  576.      * @param array    $arr       (reference) array where data from the row
  577.      *                            should be placed
  578.      * @param int      $fetchmode how the resulting array should be indexed
  579.      * @param int      $rownum    the row number to fetch
  580.      *
  581.      * @return mixed DB_OK on success, NULL when end of result set is
  582.      *               reached or on failure
  583.      *
  584.      * @access private
  585.      * @see DB_result::fetchInto()
  586.      */
  587.     function fetchInto($result, &$arr, $fetchmode, $rownum=null)
  588.     {
  589.         if ($rownum !== null) {
  590.             if (!@odbtp_data_seek($result, $rownum)) {
  591.                 return null;
  592.             }
  593.         }
  594.         if ($fetchmode & DB_FETCHMODE_ASSOC) {
  595.             $arr = @odbtp_fetch_assoc($result);
  596.             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  597.                 $arr = array_change_key_case($arr, CASE_LOWER);
  598.             }
  599.         } else {
  600.             $arr = @odbtp_fetch_row($result);
  601.         }
  602.         if (!$arr) {
  603.             return null;
  604.         }
  605.         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  606.             $this->_rtrimArrayValues($arr);
  607.         }
  608.         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  609.             $this->_convertNullArrayValuesToEmpty($arr);
  610.         }
  611.         return DB_OK;
  612.     }
  613.  
  614.     // }}}
  615.     // {{{ freeResult()
  616.  
  617.     /**
  618.      * Free the internal resources associated with $result.
  619.      *
  620.      * @param resource $result ODBTP query result identifier
  621.      *
  622.      * @return boolean TRUE on success, FALSE if $result is invalid
  623.      *
  624.      * @access public
  625.      */
  626.     function freeResult($result)
  627.     {
  628.         return @odbtp_free_query($result);
  629.     }
  630.  
  631.     // }}}
  632.     // {{{ numCols()
  633.  
  634.     /**
  635.      * Returns the number of columns in a result
  636.      *
  637.      * @param resource $result ODBTP query result identifier
  638.      *
  639.      * @return mixed DB_Error or the number of columns
  640.      *
  641.      * @access public
  642.      */
  643.     function numCols($result)
  644.     {
  645.         $cols = @odbtp_num_fields($result);
  646.         if ($cols === false) {
  647.             return $this->odbtpRaiseError();
  648.         }
  649.         return $cols;
  650.     }
  651.  
  652.     // }}}
  653.     // {{{ numRows()
  654.  
  655.     /**
  656.      * Returns the number of rows in a result if row caching has been
  657.      * enabled.
  658.      *
  659.      * @param resource $result ODBTP query result identifier
  660.      *
  661.      * @return mixed DB_Error or the number of rows
  662.      *
  663.      * @access public
  664.      */
  665.     function numRows($result)
  666.     {
  667.         $rows = @odbtp_num_rows($result);
  668.         if ($rows === false) {
  669.             return $this->odbtpRaiseError();
  670.         }
  671.         return $rows;
  672.     }
  673.  
  674.     // }}}
  675.     // {{{ autoCommit()
  676.  
  677.     /**
  678.      * enable automatic Commit
  679.      *
  680.      * @param boolean $onoff
  681.      * @return mixed DB_Error
  682.      *
  683.      * @access public
  684.      */
  685.     function autoCommit($onoff = false)
  686.     {
  687.         $txn = $onoff ? ODB_TXN_NONE : $this->txn_isolation;
  688.         if (!@odbtp_set_attr(ODB_ATTR_TRANSACTIONS, $txn, $this->connection)) {
  689.             return $this->odbtpRaiseError();
  690.         }
  691.         return DB_OK;
  692.     }
  693.  
  694.     // }}}
  695.     // {{{ commit()
  696.  
  697.     /**
  698.      * Commit the current transaction.
  699.      *
  700.      * @return mixed DB_Error
  701.      *
  702.      * @access public
  703.      */
  704.     function commit()
  705.     {
  706.         if (!@odbtp_commit($this->connection)) {
  707.             return $this->odbtpRaiseError();
  708.         }
  709.         return DB_OK;
  710.     }
  711.  
  712.     // }}}
  713.     // {{{ rollback()
  714.  
  715.     /**
  716.      * Rollback the current transaction.
  717.      *
  718.      * @return mixed DB_Error
  719.      *
  720.      * @access public
  721.      */
  722.     function rollback()
  723.     {
  724.         if (!@odbtp_rollback($this->connection)) {
  725.             return $this->odbtpRaiseError();
  726.         }
  727.         return DB_OK;
  728.     }
  729.  
  730.     // }}}
  731.     // {{{ affectedRows()
  732.  
  733.     /**
  734.      * Returns the affected rows of a query
  735.      *
  736.      * @return mixed DB_Error or the number of rows
  737.      *
  738.      * @access public
  739.      */
  740.     function affectedRows()
  741.     {
  742.         if (is_resource($this->query_result)) {
  743.             $rows = @odbtp_affected_rows($this->query_result);
  744.             if ($rows === false) {
  745.                 return $this->odbtpRaiseError();
  746.             }
  747.         } else {
  748.             $rows = -1;
  749.         }
  750.         return $rows;
  751.     }
  752.  
  753.     // }}}
  754.     // {{{ nextId()
  755.  
  756.     /**
  757.      * Returns the next free id in a sequence
  758.      *
  759.      * @param string  $seq_name  name of the sequence
  760.      * @param boolean $ondemand  when true, the seqence is automatically
  761.      *                           created if it does not exist
  762.      *
  763.      * @return int  the next id number in the sequence.  DB_Error if problem.
  764.      *
  765.      * @access public
  766.      * @internal
  767.      * @see DB_common::nextID()
  768.      */
  769.     function nextId($seq_name, $ondemand = true)
  770.     {
  771.         $seqname = $this->getSequenceName($seq_name);
  772.         $repeat = 0;
  773.         do {
  774.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  775.             $result = $this->query("UPDATE ${seqname} SET id = id + 1");
  776.             $this->popErrorHandling();
  777.             if ($ondemand && DB::isError($result) &&
  778.                 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
  779.                 $repeat = 1;
  780.                 $this->pushErrorHandling(PEAR_ERROR_RETURN);
  781.                 $result = $this->createSequence($seq_name);
  782.                 $this->popErrorHandling();
  783.                 if (DB::isError($result)) {
  784.                     return $this->raiseError($result);
  785.                 }
  786.                 $result = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
  787.             } else {
  788.                 $repeat = 0;
  789.             }
  790.         } while ($repeat);
  791.  
  792.         if (DB::isError($result)) {
  793.             return $this->raiseError($result);
  794.         }
  795.         $result = $this->query("SELECT id FROM ${seqname}");
  796.         if (DB::isError($result)) {
  797.             return $result;
  798.         }
  799.         $row = $result->fetchRow(DB_FETCHMODE_ORDERED);
  800.         if (DB::isError($row || !$row)) {
  801.             return $row;
  802.         }
  803.         return $row[0];
  804.     }
  805.  
  806.     // }}}
  807.     // {{{ createSequence()
  808.  
  809.     /**
  810.      * Creates a new sequence
  811.      *
  812.      * @param string $seq_name  name of the new sequence
  813.      *
  814.      * @return int  DB_OK on success.  A DB_Error object is returned if
  815.      *              problems arise.
  816.      *
  817.      * @access public
  818.      * @internal
  819.      * @see DB_common::createSequence()
  820.      */
  821.     function createSequence($seq_name)
  822.     {
  823.         $seqname = $this->getSequenceName($seq_name);
  824.  
  825.         // Make sure Visual FoxPro creates table in database folder.
  826.         if( $this->dbsyntax == 'vfp' ) {
  827.             $path = @odbtp_get_attr(ODB_ATTR_DATABASENAME, $this->connection);
  828.             //if using vfp dbc file
  829.             if( !strcasecmp(strrchr($path, '.'), '.dbc') )
  830.                 $path = substr($path,0,strrpos($path,"\\"));
  831.             $seqname = $path . "\\" . $seqname;
  832.         }
  833.         return $this->query("CREATE TABLE ${seqname} ".
  834.                             '(id INT NOT NULL UNIQUE)');
  835.     }
  836.  
  837.     // }}}
  838.     // {{{ dropSequence()
  839.  
  840.     /**
  841.      * Deletes a sequence
  842.      *
  843.      * @param string $seq_name  name of the sequence to be deleted
  844.      *
  845.      * @return int  DB_OK on success.  DB_Error if problems.
  846.      *
  847.      * @access public
  848.      * @internal
  849.      * @see DB_common::dropSequence()
  850.      */
  851.     function dropSequence($seq_name)
  852.     {
  853.         $seqname = $this->getSequenceName($seq_name);
  854.  
  855.         // Make sure Visual FoxPro drops table in database folder.
  856.         if( $this->dbsyntax == 'vfp' ) {
  857.             $path = @odbtp_get_attr(ODB_ATTR_DATABASENAME, $this->connection);
  858.             //if using vfp dbc file
  859.             if( !strcasecmp(strrchr($path, '.'), '.dbc') )
  860.                 $path = substr($path,0,strrpos($path,"\\"));
  861.             $seqname = $path . "\\" . $seqname;
  862.         }
  863.         return $this->query("DROP TABLE ${seqname}");
  864.     }
  865.  
  866.     // }}}
  867.     // {{{ quote()
  868.  
  869.     /**
  870.      * DEPRECATED: Quotes a string so it can be safely used in a query
  871.      *
  872.      * @param string $string the input string to quote
  873.      *
  874.      * @return string The NULL string or the string quotes
  875.      *                in magic_quote_sybase style
  876.      *
  877.      * @access public
  878.      * @see DB_common::quoteSmart(), DB_common::escapeSimple()
  879.      * @deprecated  Deprecated in release 1.6.0
  880.      * @internal
  881.      */
  882.     function quote($str = null)
  883.     {
  884.         switch (strtolower(gettype($str))) {
  885.             case 'null':
  886.                 return 'NULL';
  887.             case 'integer':
  888.             case 'double':
  889.                 return $str;
  890.             case 'string':
  891.             default:
  892.                 $str = str_replace("'", "''", $str);
  893.                 return "'$str'";
  894.         }
  895.     }
  896.  
  897.     // }}}
  898.     // {{{ errorNative()
  899.  
  900.     /**
  901.      * Get the native error code of the last error (if any) that
  902.      * occured on the current connection.
  903.      *
  904.      * @return string native ODBTP error message
  905.      *
  906.      * @access public
  907.      */
  908.     function errorNative()
  909.     {
  910.         if (!isset($this->connection) || !is_resource($this->connection)) {
  911.             return @odbtp_last_error();
  912.         }
  913.         return @odbtp_last_error($this->connection);
  914.     }
  915.  
  916.     // }}}
  917.     // {{{ odbtpRaiseError()
  918.  
  919.     /**
  920.      * Gather information about an error, then use that info to create a
  921.      * DB error object and finally return that object.
  922.      *
  923.      * @param  integer  $code  PEAR error number (usually a DB constant) if
  924.      *                         manually raising an error
  925.      *
  926.      * @return object  DB error object
  927.      *
  928.      * @access public
  929.      * @see errorCode()
  930.      * @see errorNative()
  931.      * @see DB_common::raiseError()
  932.      */
  933.     function odbtpRaiseError($code = null)
  934.     {
  935.         if ($code === null) {
  936.             $code = $this->errorCode(@odbtp_last_error_state());
  937.         }
  938.         return $this->raiseError($code, null, null, null, @odbtp_last_error());
  939.     }
  940.  
  941.     // }}}
  942.     // {{{ tableInfo()
  943.  
  944.     /**
  945.      * Returns information about a table or a result set
  946.      *
  947.      * The format of the resulting array depends on which <var>$mode</var>
  948.      * you select.  The sample output below is based on this query:
  949.      * <pre>
  950.      *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
  951.      *    FROM tblFoo
  952.      *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
  953.      * </pre>
  954.      *
  955.      * <ul>
  956.      * <li>
  957.      *
  958.      * <kbd>null</kbd> (default)
  959.      *   <pre>
  960.      *   [0] => Array (
  961.      *       [table] => tblFoo
  962.      *       [name] => fldId
  963.      *       [type] => int
  964.      *       [len] => 11
  965.      *       [flags] => primary_key not_null
  966.      *   )
  967.      *   [1] => Array (
  968.      *       [table] => tblFoo
  969.      *       [name] => fldPhone
  970.      *       [type] => string
  971.      *       [len] => 20
  972.      *       [flags] =>
  973.      *   )
  974.      *   [2] => Array (
  975.      *       [table] => tblBar
  976.      *       [name] => fldId
  977.      *       [type] => int
  978.      *       [len] => 11
  979.      *       [flags] => primary_key not_null
  980.      *   )
  981.      *   </pre>
  982.      *
  983.      * </li><li>
  984.      *
  985.      * <kbd>DB_TABLEINFO_ORDER</kbd>
  986.      *
  987.      *   <p>In addition to the information found in the default output,
  988.      *   a notation of the number of columns is provided by the
  989.      *   <samp>num_fields</samp> element while the <samp>order</samp>
  990.      *   element provides an array with the column names as the keys and
  991.      *   their location index number (corresponding to the keys in the
  992.      *   the default output) as the values.</p>
  993.      *
  994.      *   <p>If a result set has identical field names, the last one is
  995.      *   used.</p>
  996.      *
  997.      *   <pre>
  998.      *   [num_fields] => 3
  999.      *   [order] => Array (
  1000.      *       [fldId] => 2
  1001.      *       [fldTrans] => 1
  1002.      *   )
  1003.      *   </pre>
  1004.      *
  1005.      * </li><li>
  1006.      *
  1007.      * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
  1008.      *
  1009.      *   <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
  1010.      *   dimensions to the array in which the table names are keys and
  1011.      *   the field names are sub-keys.  This is helpful for queries that
  1012.      *   join tables which have identical field names.</p>
  1013.      *
  1014.      *   <pre>
  1015.      *   [num_fields] => 3
  1016.      *   [ordertable] => Array (
  1017.      *       [tblFoo] => Array (
  1018.      *           [fldId] => 0
  1019.      *           [fldPhone] => 1
  1020.      *       )
  1021.      *       [tblBar] => Array (
  1022.      *           [fldId] => 2
  1023.      *       )
  1024.      *   )
  1025.      *   </pre>
  1026.      *
  1027.      * </li>
  1028.      * </ul>
  1029.      *
  1030.      * The <samp>flags</samp> element contains a space separated list
  1031.      * of extra information about the field.  This data is inconsistent
  1032.      * between DBMS's due to the way each DBMS works.
  1033.      *   + <samp>primary_key</samp>
  1034.      *   + <samp>unique_key</samp>
  1035.      *   + <samp>multiple_key</samp>
  1036.      *   + <samp>not_null</samp>
  1037.      *
  1038.      * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
  1039.      * elements if <var>$result</var> is a table name.  The following DBMS's
  1040.      * provide full information from queries:
  1041.      *   + fbsql
  1042.      *   + mysql
  1043.      *
  1044.      * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
  1045.      * turned on, the names of tables and fields will be lowercased.
  1046.      *
  1047.      * @param object|string  $result  DB_result object from a query or a
  1048.      *                                string containing the name of a table.
  1049.      *                                While this also accepts a query result
  1050.      *                                resource identifier, this behavior is
  1051.      *                                deprecated.
  1052.      * @param int  $mode   either unused or one of the tableInfo modes:
  1053.      *                     <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
  1054.      *                     <kbd>DB_TABLEINFO_ORDER</kbd> or
  1055.      *                     <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
  1056.      *                     These are bitwise, so the first two can be
  1057.      *                     combined using <kbd>|</kbd>.
  1058.      * @return array  an associative array with the information requested.
  1059.      *                If something goes wrong an error object is returned.
  1060.      *
  1061.      * @access public
  1062.      * @see DB_common::setOption()
  1063.      */
  1064.     function tableInfo($result, $mode = null)
  1065.     {
  1066.         if (is_string($result)) {
  1067.             /*
  1068.              * Probably received a table name.
  1069.              */
  1070.             $is_table = true;
  1071.         } else if (isset($result->result)) {
  1072.             /*
  1073.              * Probably received a result object.
  1074.              * Extract the result resource identifier.
  1075.              */
  1076.             $id = $result->result;
  1077.             $is_table = false;
  1078.         } else {
  1079.             /*
  1080.              * Probably received a result resource identifier.
  1081.              * Copy it.
  1082.              * Deprecated.  Here for compatibility only.
  1083.              */
  1084.             $id = $result;
  1085.             $is_table = false;
  1086.         }
  1087.  
  1088.         $res = array();
  1089.  
  1090.         if ($mode) {
  1091.             $res['num_fields'] = 0;
  1092.         }
  1093.  
  1094.         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  1095.             $case_func = 'strtolower';
  1096.         } else {
  1097.             $case_func = 'strval';
  1098.         }
  1099.  
  1100.         // if $result is a string, then we want information about a
  1101.         // table without a resultset
  1102.  
  1103.         if ($is_table) {
  1104.             // Extract schema name if present
  1105.             if (($at = strpos($result,'.')) !== false) {
  1106.                 $schema = substr( $result, 0, $at );
  1107.                 $table  = substr( $result, $at+1 );
  1108.             }
  1109.             else {
  1110.                 $schema = '';
  1111.                 $table  = $result;
  1112.             }
  1113.             $id = @odbtp_query("||SQLColumns||$schema|$table", $this->connection);
  1114.             if (!$id) {
  1115.                 return $this->odbtpRaiseError();
  1116.             }
  1117.             for ($i=0; ($row = @odbtp_fetch_row($id)); $i++) {
  1118.                 $res[$i]['table'] = $case_func($row[2]);
  1119.                 $res[$i]['name']  = $case_func($row[3]);
  1120.                 $res[$i]['type']  = $case_func($row[5]);
  1121.                 $res[$i]['len']   = $row[6];
  1122.                 $res[$i]['flags'] = @odbtp_flags($row[4], $row[5], $row[10]);
  1123.             }
  1124.             $count = $i;
  1125.             $id = @odbtp_query("||SQLPrimaryKeys|$db||$result", $this->connection);
  1126.             if ($id) {
  1127.                 while ($row = @odbtp_fetch_row($id)) {
  1128.                     for ($i=0; $i<$count; $i++) {
  1129.                         if (!strcasecmp($row[3], $res[$i]['name'])) {
  1130.                             if ($res[$i]['flags'])
  1131.                                 $res[$i]['flags'] .= ' primary_key';
  1132.                             else
  1133.                                 $res[$i]['flags'] .= 'primary_key';
  1134.                             break;
  1135.                         }
  1136.                     }
  1137.                 }
  1138.                 @odbtp_free_query($id);
  1139.             }
  1140.         } else { // else we want information about a resultset
  1141.             $count = @odbtp_num_fields($id);
  1142.             for ($i=0; $i<$count; $i++) {
  1143.                 $res[$i]['table'] = $case_func(@odbtp_field_table($id, $i));
  1144.                 $res[$i]['name']  = $case_func(@odbtp_field_name($id, $i));
  1145.                 $res[$i]['type']  = $case_func(@odbtp_field_type($id, $i));
  1146.                 $res[$i]['len']   = @odbtp_field_length($id, $i);
  1147.                 $res[$i]['flags'] = @odbtp_field_flags($id, $i);
  1148.             }
  1149.         }
  1150.         if ($mode) { // full
  1151.             $res['num_fields'] = $count;
  1152.  
  1153.             for ($i=0; $i<$count; $i++) {
  1154.                 if ($mode & DB_TABLEINFO_ORDER) {
  1155.                     $res['order'][$res[$i]['name']] = $i;
  1156.                 }
  1157.                 if ($mode & DB_TABLEINFO_ORDERTABLE) {
  1158.                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  1159.                 }
  1160.             }
  1161.         }
  1162.         return $res;
  1163.     }
  1164.  
  1165.     // }}}
  1166.     // {{{ getSpecialQuery()
  1167.  
  1168.     /**
  1169.      * Returns the query needed to get some backend info
  1170.      *
  1171.      * @param string $type What kind of info you want to retrieve
  1172.      *
  1173.      * @return string The SQL query string
  1174.      *
  1175.      * @access public
  1176.      */
  1177.     function getSpecialQuery($type)
  1178.     {
  1179.         switch ($this->dbsyntax) {
  1180.             case 'mssql':
  1181.             case 'sybase':
  1182.                 switch ($type) {
  1183.                     case 'tables':
  1184.                         $sql = "select name from sysobjects where type = 'U' order by name";
  1185.                         break;
  1186.                     case 'views':
  1187.                         $sql = "select name from sysobjects where type = 'V' order by name";
  1188.                         break;
  1189.                     case 'tables':
  1190.                         $sql = "SELECT name FROM sysobjects WHERE type = 'U'"
  1191.                              . ' ORDER BY name';
  1192.                         break;
  1193.                     case 'views':
  1194.                         $sql = "SELECT name FROM sysobjects WHERE type = 'V'"
  1195.                              . ' ORDER BY name';
  1196.                         break;
  1197.                     default:
  1198.                         return null;
  1199.                 }
  1200.                 break;
  1201.             case 'oracle':
  1202.                 switch ($type) {
  1203.                     case 'tables':
  1204.                         $sql = "SELECT table_name FROM user_tables";
  1205.                         break;
  1206.                     case 'synonyms':
  1207.                         $sql = 'SELECT synonym_name FROM user_synonyms';
  1208.                         break;
  1209.                     default:
  1210.                         return null;
  1211.                 }
  1212.                 break;
  1213.             case 'mysql':
  1214.                 switch ($type) {
  1215.                     case 'tables':
  1216.                         $sql = "SHOW TABLES";
  1217.                         break;
  1218.                     case 'users':
  1219.                         $sql =  'SELECT DISTINCT User FROM mysql.user';
  1220.                         break;
  1221.                     case 'databases':
  1222.                         $sql = "SHOW DATABASES";
  1223.                         break;
  1224.                     default:
  1225.                         return null;
  1226.                 }
  1227.                 break;
  1228.             default:
  1229.                 return null;
  1230.         }
  1231.         return $sql;
  1232.     }
  1233.  
  1234.     // }}}
  1235.     // {{{ quoteIdentifier()
  1236.  
  1237.     /**
  1238.      * Quote a string so it can be safely used as a table / column name
  1239.      *
  1240.      * Quoting style depends on the database syntax in use.
  1241.      *
  1242.      * @param string $str identifier name to be quoted
  1243.      *
  1244.      * @return string quoted identifier string
  1245.      *
  1246.      * @access public
  1247.      */
  1248.     function quoteIdentifier($str)
  1249.     {
  1250.         switch ($this->dbsyntax) {
  1251.             case 'access':
  1252.                 return '[' . $str . ']';
  1253.             case 'mssql':
  1254.             case 'sybase':
  1255.                 return '[' . str_replace(']', ']]', $str) . ']';
  1256.             case 'mysql':
  1257.             case 'mysqli':
  1258.                 return '`' . $str . '`';
  1259.         }
  1260.         return '"' . str_replace('"', '""', $str) . '"';
  1261.     }
  1262.  
  1263.     // }}}
  1264.     // {{{ getConnectionId()
  1265.  
  1266.     /**
  1267.      * Get ODBTP Connection Id string
  1268.      *
  1269.      * @return mixed Connection Id string, or DB_Error if failure
  1270.      *
  1271.      * @access public
  1272.      */
  1273.     function getConnectionId()
  1274.     {
  1275.         $id = @odbtp_connect_id($this->connection);
  1276.         if (!is_string($id)) {
  1277.             return $this->odbtpRaiseError();
  1278.         }
  1279.         return $id;
  1280.     }
  1281.  
  1282.     // }}}
  1283.  
  1284. }
  1285.  
  1286. /*
  1287.  * Local variables:
  1288.  * tab-width: 4
  1289.  * c-basic-offset: 4
  1290.  * End:
  1291.  */
  1292.  
  1293. ?>
  1294.